package org.lecric.wind44.client.widget;

import java.util.List;

import org.lecric.wind44.client.SpotUtils;
import org.lecric.wind44.client.dialog.ReorderDialog;
import org.lecric.wind44.client.model.ISpot;
import org.lecric.wind44.client.model.Spot;
import org.lecric.wind44.client.model.SpotInformation;
import org.lecric.wind44.client.model.Spots;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

public class EditionPanel {
	private static final int ADMIN_COLUMN_INDEX = 4;
	private static final String SPOT_EXAMPLE = "(ie.: Mazerolles, Pont Mahe)";
	private static final String WEBSITE_EXAMPLE = "(ie.: Funboard 44, WindGuru)";
	private static final String WEBSITE = "Website";
	private static final String SPOT = "Spot";
	private static final String NAME = " name ";
	private static final String ENTER_A = "Enter a ";
	//	private static final String MORE_ITEM = "+";
	private Button renameSpotButton;
	private Button renameInfoButton;
	private Button newSpotButton;
	private Button newInfoButton;
	private Button makePublicSpotButton;
	private Button makePublicSpotInfoButton;
	private Button deleteSpotButton;
	private Button deleteSpotInfoButton;
	private Button positionSpotButton;
	private Button reorderSpotButton;
	private Button reorderInfosButton;
	private ListBox spotList;
	private ListBox infoList;
	private final List<Spot> spots;
	private TextBox urlText;
	private Button modifyButton;
	private final boolean administrator;
	private final String user;

	private abstract class GenericClickHandler implements ClickHandler, AsyncCallback<Void>
	{
		@Override
		public void onClick(ClickEvent event)
		{
			execute();
			int index=spotList.getSelectedIndex();
			if(index!=-1 && index < spots.size())
			{
				Spot spot=SpotUtils.sort(spots).get(index);
				execute(spot);
				SpotInformation spotInformation=null;
				index=infoList.getSelectedIndex();
				if(index!=-1 && index < spot.getInfos().size())
				{
					spotInformation=SpotUtils.sort(spot.getInfos()).get(index);
					execute(spot, spotInformation);
				}
			}
		}

		protected void execute() {}
		protected void execute(Spot spot) {}
		protected void execute(Spot spot, SpotInformation spotInfo) {}

		@Override
		public void onFailure(Throwable caught) {
			GWT.log("Failed changing properties", caught);
		}

		@Override
		public void onSuccess(Void result) { }
	}

	public EditionPanel(Spots spots)
	{
		this.spots = spots.getSpots();
		this.administrator = spots.isAdministrator();
		this.user = spots.getUser();
	}

	public Widget createPanel()
	{
		final Grid grid=new Grid(5, 8);
		spotList = new ListBox(false);
		newSpotButton=new Button("New");
		newSpotButton.setTitle("New spot");
		renameSpotButton=new Button("Rename");
		makePublicSpotButton = new Button("Make Public");
		deleteSpotButton = new Button("X");
		positionSpotButton=new Button("Position");
		reorderSpotButton=new Button("Order");
		newInfoButton=new Button("New");
		newInfoButton.setTitle("New web site");
		renameInfoButton=new Button("Rename");
		makePublicSpotInfoButton = new Button("Make Public");
		deleteSpotInfoButton = new Button("X");
		reorderInfosButton=new Button("Order");
		modifyButton = new Button("Modify URL");
		infoList = new ListBox(false);
		urlText = new TextBox();
		urlText.setWidth("350px");

		newSpotButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute() {
				String name=Window.prompt(ENTER_A + SPOT + NAME + SPOT_EXAMPLE, "");
				if(checkName(spots, name))
				{
					Spot spot=new Spot(-1, name, false, null, null, null, spots.size());
					SpotUtils.SPOT_SERVICE.add(spot, new AsyncCallback<Spot>() {
						@Override
						public void onFailure(Throwable caught) {
							// TODO Auto-generated method stub
							GWT.log("Error adding spot", caught);
						}
						@Override
						public void onSuccess(Spot result) {
							spots.add(result);
							spotList.addItem(format(result));
							spotList.setSelectedIndex(spotList.getItemCount()-1);
							updateInfoListInCascade();
//							mailNotify("NEW: Spot " + result.getName());
						}
					});
				}
			}
		});
		renameSpotButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute(final Spot spot) {
				String name=Window.prompt(ENTER_A + SPOT + NAME + SPOT_EXAMPLE, spot.getName());
				if(checkName(spots, name))
				{
					spot.setName(name);
					SpotUtils.SPOT_SERVICE.changeProperties(spot, this);
					replace(spotList, spot);
				}
			}
		});
		makePublicSpotButton.addClickHandler(new GenericClickHandler() {
			private Spot spot2update;
			@Override
			protected void execute(Spot spot) {
				this.spot2update=spot;
				spot.setPub(true);
				SpotUtils.SPOT_SERVICE.changeProperties(spot, this);
			}
			@Override
			public void onSuccess(Void result) {
				replace(spotList, spot2update);
			}
		});
		deleteSpotButton.addClickHandler(new GenericClickHandler() {
			private Spot spot2delete;
			@Override
			protected void execute(Spot spot) {
				spot2delete=spot;
				if(Window.confirm("Sure ?"))
					SpotUtils.SPOT_SERVICE.delete(spot, this);
			}
			@Override
			public void onSuccess(Void result) {
				spotList.removeItem(spotList.getSelectedIndex());
				spots.remove(spot2delete);
			}
		});
		positionSpotButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute(Spot spot) {
				String latitude=Window.prompt("Latitude", spot.getLatitude() == null ? "" : Double.toString(spot.getLatitude()));
				if(latitude!=null)
				{
					double latitudeD=Double.parseDouble(latitude);
					String longitude=Window.prompt("Longitude", spot.getLongitude() == null ? "" : Double.toString(spot.getLongitude()));
					double longitudeD=Double.parseDouble(longitude);
					spot.setLatitude(new Double(latitudeD));
					spot.setLongitude(new Double(longitudeD));
					SpotUtils.SPOT_SERVICE.changeProperties(spot, this);
				}
			}
		});
		reorderSpotButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute() {
				final AsyncCallback<Void> thiz=this;
				ReorderDialog dialog=new ReorderDialog(spots, "Order the spots:") {
					@Override
					protected void validate() {
						for(Spot spot: spots)
							SpotUtils.SPOT_SERVICE.changeProperties(spot, thiz);
						updateSpotsInCascade();
					}
				};
				dialog.show();
			}
		});
		reorderInfosButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute(final Spot spot) {
				final AsyncCallback<Void> thiz=this;
				ReorderDialog dialog=new ReorderDialog(spot.getInfos(), "Order the Web sites:") {
					@Override
					protected void validate() {
						for(SpotInformation info: spot.getInfos())
							SpotUtils.SPOT_SERVICE.changeProperties(info, thiz);
						updateInfoListInCascade();
					}
				};
				dialog.show();
			}
		});
		newInfoButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute(final Spot spot) {
				String name=Window.prompt(ENTER_A + WEBSITE + NAME + WEBSITE_EXAMPLE, "");
				if(checkName(spot.getInfos(), name))
				{
					SpotInformation spotInformation=new SpotInformation(-1, name, "", false, null, spot.getInfos().size());
					SpotUtils.SPOT_SERVICE.add(spot, spotInformation, new AsyncCallback<SpotInformation>() {
						@Override
						public void onFailure(Throwable caught) {
							// TODO Auto-generated method stub
							GWT.log("Error adding value", caught);
						}
						@Override
						public void onSuccess(SpotInformation result) {
							spot.getInfos().add(result);
							infoList.addItem(format(result));
							infoList.setSelectedIndex(infoList.getItemCount()-1);
							updateUrl();
						}
					});
				}
			}
		});
		renameInfoButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute(Spot spot, SpotInformation spotInfo) {
				String name=Window.prompt(ENTER_A + WEBSITE + NAME + WEBSITE_EXAMPLE, spotInfo.getName());
				if(checkName(spot.getInfos(), name))
				{
					spotInfo.setName(name);
					SpotUtils.SPOT_SERVICE.changeProperties(spotInfo, this);
					replace(infoList, spotInfo);
				}
			}
		});
		makePublicSpotInfoButton.addClickHandler(new GenericClickHandler() {
			private SpotInformation info2update;
			@Override
			protected void execute(Spot spot, SpotInformation spotInfo) {
				this.info2update=spotInfo;
				spotInfo.setPub(true);
				SpotUtils.SPOT_SERVICE.changeProperties(spotInfo, this);
			}
			@Override
			public void onSuccess(Void result) {
				replace(infoList, info2update);
			}
		});
		deleteSpotInfoButton.addClickHandler(new GenericClickHandler() {
			private Spot spot;
			private SpotInformation info2delete;
			@Override
			protected void execute(Spot spot, SpotInformation spotInfo) {
				this.spot=spot;
				info2delete=spotInfo;
				if(Window.confirm("Sure ?"))
					SpotUtils.SPOT_SERVICE.delete(spotInfo, this);
			}
			@Override
			public void onSuccess(Void result) {
				infoList.removeItem(infoList.getSelectedIndex());
				spot.getInfos().remove(info2delete);
			}
		});
		modifyButton.addClickHandler(new GenericClickHandler() {
			@Override
			protected void execute(Spot spot, SpotInformation spotInfo) {
				spotInfo.setUrl(urlText.getText());
				SpotUtils.SPOT_SERVICE.changeProperties(spotInfo, this);
			}

			@Override
			public void onSuccess(Void arg0)
			{
				super.onSuccess(arg0);
			}
		});
		final Button initDBButton = new Button("Re-init DB");
		initDBButton.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				if(Window.confirm("Sure ?"))
				{
					SpotUtils.SPOT_SERVICE.initDB(new AsyncCallback<Void>(){
						@Override
						public void onFailure(Throwable caught) {
							GWT.log("Error", caught);
						}
						@Override
						public void onSuccess(Void result) {
							GWT.log("Init DB ok", null);		
						}
					});
				}
			}
		});
		if(administrator)
		{
			grid.setWidget(0, ADMIN_COLUMN_INDEX, makePublicSpotButton);
			grid.setWidget(0, ADMIN_COLUMN_INDEX + 1, deleteSpotButton);
			grid.setWidget(0, ADMIN_COLUMN_INDEX + 2, reorderSpotButton);
			grid.setWidget(0, ADMIN_COLUMN_INDEX + 3, positionSpotButton);
			grid.setWidget(1, ADMIN_COLUMN_INDEX, makePublicSpotInfoButton);
			grid.setWidget(1, ADMIN_COLUMN_INDEX + 1, deleteSpotInfoButton);
			grid.setWidget(1, ADMIN_COLUMN_INDEX + 2, reorderInfosButton);
			grid.setWidget(4, 0, initDBButton);
		}
//		updateSpotsInCascade();
		//		spotList.addItem(MORE_ITEM);
		spotList.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				int index=spotList.getSelectedIndex();
				if(index < spots.size())
				{
					updateInfoListInCascade();
				}
			}
		});
		infoList.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				int index=spotList.getSelectedIndex();
				if(index!=-1)
					updateUrl();
			}
		});
		updateSpotsInCascade();
		grid.setWidget(0, 0, new Label(SPOT + " " + SPOT_EXAMPLE+": "));
		grid.setWidget(0, 1, spotList);
		grid.setWidget(0, 2, newSpotButton);
		grid.setWidget(0, 3, renameSpotButton);
		grid.setWidget(1, 0, new Label(WEBSITE + " " + WEBSITE_EXAMPLE+": "));
		grid.setWidget(1, 1, infoList);
		grid.setWidget(1, 2, newInfoButton);
		grid.setWidget(1, 3, renameInfoButton);
		grid.setWidget(2, 0, new Label("Url (ie.: http://maree.frbateaux.net/111): "));
		grid.setWidget(2, 1, urlText);
		grid.setWidget(3, 0, new Label(""));
		grid.setWidget(3, 1, modifyButton);
		return grid;
	}

	private void updateSpotsInCascade() {
		while(spotList.getItemCount()>0)
			spotList.removeItem(0);

		for(Spot spot: SpotUtils.sort(spots))
		{
			spotList.addItem(format(spot));
		}
		updateInfoListInCascade();
	}

	private void updateInfoListInCascade() {
		int index=spotList.getSelectedIndex();
		if(index!=-1)
		{
			Spot spot=SpotUtils.sort(spots).get(index);
			makePublicSpotButton.setEnabled(!spot.isPub());
			while(infoList.getItemCount()>0)
			{
				infoList.removeItem(0);
			}
			for(SpotInformation info: SpotUtils.sort(spot.getInfos()))
			{
				infoList.addItem(format(info));
			}
			renameSpotButton.setEnabled(canModify(spot));
			renameSpotButton.setTitle("Rename spot " + spot.getName());
		}
		else
		{
			makePublicSpotButton.setEnabled(false);
			renameSpotButton.setEnabled(false);
		}
		updateUrl();
	}

	private void updateUrl() {
		int index=spotList.getSelectedIndex();
		if(index!=-1)
		{
			Spot spot=SpotUtils.sort(spots).get(index);
			index=infoList.getSelectedIndex();
			if(index!=-1)
			{
				SpotInformation spotInformation = SpotUtils.sort(spot.getInfos()).get(index);
				makePublicSpotInfoButton.setEnabled(!spotInformation.isPub());
				boolean canModify = canModify(spotInformation);
				urlText.setReadOnly(!canModify);
				modifyButton.setEnabled(canModify);
				renameInfoButton.setEnabled(canModify);
				renameInfoButton.setTitle("Rename web site " + spotInformation.getName());
				urlText.setValue(spotInformation.getUrl());
				return;
			}
		}
		// Problem:
		makePublicSpotInfoButton.setEnabled(false);
		modifyButton.setEnabled(false);
		renameInfoButton.setEnabled(false);
		urlText.setValue("");
	}

	private void replace(ListBox spotList, ISpot spot) {
		int index = spotList.getSelectedIndex();
		spotList.removeItem(index);
		spotList.insertItem(format(spot), index);
		spotList.setSelectedIndex(index);
	}

	private boolean canModify(ISpot spot) {
		return (!spot.isPub()) || administrator;
	}

	private boolean checkName(List<? extends ISpot> spots, String name)
	{
		if(name==null)
		{
			// ko
			return false;
		}
		if(name.trim().isEmpty())
		{
			// ko
			Window.alert("Name cannot be empty.");
			return false;
		}

		for(ISpot spot: spots)
		{
			if(spot.getName().equals(name))
			{
				// ko
				Window.alert("This name already exists.");
				return false;
			}
		}
		// ok
		return true;
	}

	private String format(ISpot info)
	{
		return info.getName() + (info.isPub() ? "" : " (private)") + ((administrator && info.getUser() != null) ? "(" + info.getUser() + ")" : "");
	}
}
